package com.gitee.sop.layui.adminserver.module.background.admin.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.sop.layui.adminserver.entrance.bo.ServiceContext;
import com.gitee.sop.layui.adminserver.module.background.admin.entity.*;
import com.gitee.sop.layui.adminserver.module.background.admin.manager.SysApplicationManager;
import com.gitee.sop.layui.adminserver.module.background.admin.mapper.SysApplicationMapper;
import com.gitee.sop.layui.adminserver.module.background.admin.service.*;
import com.gitee.sop.layui.adminserver.module.base.error.ServiceInterruptionException;
import com.gitee.sop.layui.adminserver.module.base.util.AssertUtil;
import com.gitee.sop.layui.adminserver.module.base.util.EntityUtil;
import com.gitee.sop.layui.adminserver.module.base.util.ObjectUtil;
import com.gitee.sop.layui.adminserver.module.base.util.SqlTreeUtil;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;

/**
 * <p>
 * 应用信息 服务实现类
 * </p>
 *
 * @author admin
 * @since 2020-04-24
 */
@Service
public class SysApplicationServiceImpl extends ServiceImpl<SysApplicationMapper, SysApplication> implements SysApplicationService {
    @Lazy
    @Resource
    private SysUserApplicationRelationService sysUserApplicationRelationService;
    @Lazy
    @Resource
    private SysApplicationManager sysApplicationManager;
    @Lazy
    @Resource
    private SysMenuService sysMenuService;
    @Lazy
    @Resource
    private SysMenuRoleService sysMenuRoleService;
    @Lazy
    @Resource
    private SysMenuRoleRelationService sysMenuRoleRelationService;
    @Lazy
    @Resource
    private SysUserMenuRoleRelationService sysUserMenuRoleRelationService;

    @Override
    public List<SysApplication> getRelationListByUserUuid(String userUuid) {
        return this.sysApplicationManager.getRelationListByUserUuid(userUuid);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysApplication add(SysApplication sysApplication, String parentAppId, String templateAppId) {
        SysUser sysUser = ServiceContext.me().getUser(SysUser.class);
        SysApplication curApp;
        if (ObjectUtil.notEmpty(parentAppId)) {
            curApp = this.sysApplicationManager.getOneByAppId(parentAppId);
            AssertUtil.notNull(curApp, ServiceInterruptionException.fail(400, "没有指定的上级应用，应用id：" + parentAppId));
            AssertUtil.isTrue(curApp.getCreatorUuid().equals(sysUser.getUuid()), ServiceInterruptionException.fail(400, "无权为" + curApp.getName() + "添加下级应用"));
        } else {
            curApp = ServiceContext.me().getCurrentApplication();
        }

        sysApplication.init().setCreatorUuid(sysUser.getUuid()).setCreatedTime(new Date());
        SqlTreeUtil.setInertInfo(curApp, sysApplication);
        //插入节点需要更树信息，以保持当前树状态
        this.baseMapper.updateTreeInfoAtInsert(curApp.getTreeGroup(), curApp.getTreeLeftTraverseNumber());
        //插入节点
        save(sysApplication);

        if (ObjectUtil.notEmpty(templateAppId)) {
            SysApplication template = this.sysApplicationManager.getOneByAppId(templateAppId);
            AssertUtil.notNull(template, ServiceInterruptionException.fail(400, "模板应用不存在"));
            //2. 复用菜单信息
            List<SysMenu> sysMenuList = sysMenuService.getListByAppId(template.getAppId());
            //3. 复用角色信息
            List<SysMenuRole> sysMenuRoleList = sysMenuRoleService.getListByAppId(template.getAppId());
            //3.1 提取角色uuid信息，用于获取角色和菜单的关联关系
            List<String> menuRoleUuidList = new ArrayList<>();
            sysMenuRoleList.forEach(sysMenuRole -> menuRoleUuidList.add(sysMenuRole.getUuid()));
            //4. 复用菜单和角色的关联关系
            List<SysMenuRoleRelation> sysMenuRoleRelationList = sysMenuRoleRelationService.getListInRoleUuid(menuRoleUuidList);

            //5. 更新菜单信息，并建立旧有菜单编码与菜单的映射关系,用于菜单和角色关联关系的使用
            Map<String, SysMenu> oldUuidToMenuMap = new HashMap<>(sysMenuList.size());
            Map<String, String> treeGroupMap = new HashMap<>(sysMenuList.size());
            sysMenuList.forEach(sysMenu -> {
                String oldUuid = sysMenu.getUuid();
                EntityUtil.initSaveInfo(sysMenu).setAppId(sysApplication.getAppId());
                String newTreeGroup = treeGroupMap.get(sysMenu.getTreeGroup());
                if (newTreeGroup == null) {
                    newTreeGroup = EntityUtil.uuid();
                    treeGroupMap.put(sysMenu.getTreeGroup(), newTreeGroup);
                }
                sysMenu.setTreeGroup(newTreeGroup);
                oldUuidToMenuMap.put(oldUuid, sysMenu);
            });
            treeGroupMap.clear();

            //6. 更新角色信息，并建立旧有角色编码和角色的映射关系，用于菜单和角色关联关系的使用
            Map<String, SysMenuRole> oldUuidToMenuRoleMap = new HashMap<>(sysMenuRoleList.size());
            sysMenuRoleList.forEach(sysMenuRole -> {
                String oldUuid = sysMenuRole.getUuid();
                EntityUtil.initSaveInfo(sysMenuRole).setAppId(sysApplication.getAppId());
                String newTreeGroup = treeGroupMap.get(sysMenuRole.getTreeGroup());
                if (newTreeGroup == null) {
                    newTreeGroup = EntityUtil.uuid();
                    treeGroupMap.put(sysMenuRole.getTreeGroup(), newTreeGroup);
                }
                sysMenuRole.setTreeGroup(newTreeGroup);
                oldUuidToMenuRoleMap.put(oldUuid, sysMenuRole);
            });

            // 7.0 建立菜单和角色的映射关系
            List<SysMenuRoleRelation> newlyMenuRoleRelationList = new ArrayList<>(sysMenuRoleRelationList.size());
            sysMenuRoleRelationList.forEach(sysMenuRoleRelation -> {
                SysMenu sysMenu = oldUuidToMenuMap.get(sysMenuRoleRelation.getMenuUuid());
                SysMenuRole sysMenuRole = oldUuidToMenuRoleMap.get(sysMenuRoleRelation.getRoleUuid());
                SysMenuRoleRelation roleRelation = new SysMenuRoleRelation(sysMenuRole.getUuid(), sysMenu.getUuid());
                newlyMenuRoleRelationList.add(roleRelation);
            });

            //5. 保存
            if (!sysMenuList.isEmpty()) {
                sysMenuService.saveBatch(sysMenuList);
            }
            if (!sysMenuRoleList.isEmpty()) {
                sysMenuRoleService.saveBatch(sysMenuRoleList);
            }
            if (!newlyMenuRoleRelationList.isEmpty()) {
                sysMenuRoleRelationService.saveBatch(newlyMenuRoleRelationList);
            }
        }

        //刷新缓存
        this.sysApplicationManager.clearCache();

        return sysApplication;
    }

    @Override
    public void assign(String userUuid, String appId) {
        if (sysUserApplicationRelationService.getOneByUserUuidAndAppId(userUuid, appId) != null) {
            return;
        }

        //TODO 检索当前用户是否有为指定用户赋值指定应用的权限；

        sysUserApplicationRelationService.save(new SysUserApplicationRelation(userUuid, appId));

        //更新当前用户可用应用缓存
        SysUser cur = ServiceContext.me().checkedUserAndGet(SysUser.class);
        if (cur.getUuid().equals(userUuid)) {
            ServiceContext.me().setRelationApplication(getRelationListByUserUuid(userUuid));
        }
        //刷新缓存
        this.sysApplicationManager.clearCache();
    }

    @Override
    public SysApplication getOneByAppIdAndCreatorUuid(String appId, String uuid) {
        return this.sysApplicationManager.getOneByAppIdAndCreatorUuid(appId, uuid);
    }

    @Override
    public Map<String, Object> wrapAppName(Map<String, Object> map) {
        return wrapAppName(Arrays.asList(map)).get(0);
    }

    @Override
    public List<Map<String, Object>> wrapAppName(List<Map<String, Object>> mapList) {
        //应用id分组
        Map<String, List<Map<String, Object>>> appIdToMapList = new HashMap<>(16);
        //应用id获取
        List<String> appIdList = new ArrayList<>(16);
        mapList.forEach(stringObjectMap -> {
            Object appId = stringObjectMap.get("appId");
            if (appId != null && appId instanceof String) {
                String appId2 = (String) appId;
                if (!appIdList.contains(appId2)) {
                    appIdList.add(appId2);
                }

                //按应用id分组，方便后续进行应用信息封装
                List<Map<String, Object>> temp = appIdToMapList.get(appId2);
                if (temp == null) {
                    temp = new ArrayList<>();
                    appIdToMapList.put(appId2, temp);
                }
                temp.add(stringObjectMap);
            }
        });

        if (appIdList.isEmpty()) {
            return mapList;
        }
        List<SysApplication> applicationList = this.sysApplicationManager.getListInAppId(appIdList);
        applicationList.forEach(sysApplication -> {
            appIdToMapList.get(sysApplication.getAppId()).forEach(map -> {
                map.put("appName", sysApplication.getName());
            });
        });

        return mapList;
    }

    @Override
    public SysApplication getOneByAppId(String appId) {
        return this.baseMapper.selectOneByAppId(appId);
    }

    @Override
    public void cancel(String userUuid, String appId) {
        this.sysUserApplicationRelationService.removeByUserUuidAndAppId(userUuid, appId);

        //删除用户和角色关联
        this.sysUserMenuRoleRelationService.removeByUserUuid(appId, userUuid);

        //刷新缓存
        this.sysApplicationManager.clearCache();
    }

    @Override
    public List<Map<String, Object>> getChildListForCurrentApplication() {
        SysApplication cur = ServiceContext.me().getCurrentApplication();
        List<SysApplication> childList = this.baseMapper.selectChildListTreeBy(cur.getTreeGroup(), cur.getTreeLevel(), cur.getTreeLeftTraverseNumber());
        List<Map<String, Object>> treeList = SqlTreeUtil.tree(childList);
        //建立树形结构，即第一层级应用的pid必须为0
        if (!treeList.isEmpty()) {
            treeList.forEach(map -> {
                int pid = Integer.parseInt(String.valueOf(map.get("pid")));
                if (pid == cur.getId().intValue()) {
                    map.put("pid", 0);
                }
            });
        }
        return treeList;
    }

    @Override
    public List<SysApplication> getListByCurrentUserCreated() {
        return this.baseMapper.selectListByCreatorUser(ServiceContext.me().checkedUserAndGet(SysUser.class).getUuid());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeById(Serializable id) {
        SysUser current = ServiceContext.me().getUser(SysUser.class);
        SysApplication sysApplication = checkedModifyPermissionAndGet(current, (int) id);
        int relationCount = sysUserApplicationRelationService.countByAppId(sysApplication.getAppId());
        AssertUtil.isTrue(relationCount <= 0, ServiceInterruptionException.fail(401, "有关联用户数据，无法删除"));

        List<SysApplication> childList = this.baseMapper.selectChildListTreeBy(sysApplication.getTreeGroup(), sysApplication.getTreeLevel(), sysApplication.getTreeLevel());
        AssertUtil.isTrue(childList.isEmpty(), ServiceInterruptionException.fail(400, "有关联的子应用，无法删除"));

        //删除关联菜单
        List<SysMenu> sysMenuList = sysMenuService.getListByAppId(sysApplication.getAppId());
        //删除关联菜单角色
        List<SysMenuRole> sysMenuRoleList = sysMenuRoleService.getListByAppId(sysApplication.getAppId());
        //删除菜单和菜单角色的关联
        List<String> menuRoleUuidList = new ArrayList<>();
        sysMenuRoleList.forEach(sysMenuRole -> menuRoleUuidList.add(sysMenuRole.getUuid()));
        List<SysMenuRoleRelation> sysMenuRoleRelationList = new ArrayList<>();
        if (!menuRoleUuidList.isEmpty()) {
            sysMenuRoleRelationList = sysMenuRoleRelationService.getListInRoleUuid(menuRoleUuidList);
        }
        if (!sysMenuList.isEmpty()) {
            sysMenuService.deleteInUuid(sysApplication.getAppId(), sysMenuList.stream().collect(ArrayList::new, (l, r) -> l.add(r.getUuid()), (l, r) -> {
            }));
        }
        if (!sysMenuRoleList.isEmpty()) {
            sysMenuRoleService.deleteInUuid(sysApplication.getAppId(), sysMenuRoleList.stream().collect(ArrayList::new, (l, r) -> l.add(r.getUuid()), (l, r) -> {
            }));
        }
        if (!sysMenuRoleRelationList.isEmpty()) {
            sysMenuRoleRelationService.removeByIds(sysMenuRoleRelationList.stream().collect(ArrayList::new, (l, r) -> l.add(r.getId()), (l, r) -> {
            }));
        }


        // TODO 删除应用关联的自定义数据

        // TODO 删除应用关联的其它数据

        //删除用户与应用的关联关系
        sysUserApplicationRelationService.removeByAppId(sysApplication.getAppId());

        //删除应用
        //删除节点后更新数据
        this.baseMapper.updateTreeInfoAtDelete(sysApplication.getTreeGroup(), sysApplication.getTreeLevel(), 1);


        //清除缓存
        this.sysApplicationManager.clearCache();

        return super.removeById(id);
    }

    @Override
    public boolean updateById(SysApplication entity) {
        SysUser current = ServiceContext.me().getUser(SysUser.class);
        SysApplication sysApplication = checkedModifyPermissionAndGet(current, entity.getId());

        sysApplication.setName(entity.getName()).setDescription(entity.getDescription()).setStatus(entity.getStatus());

        if (sysApplication.isDisable()) {
            //应用已被禁用，通知禁用过滤器，进行当前应用操作的拦截
            this.sysApplicationManager.putDisable(sysApplication.getAppId());
        } else {
            this.sysApplicationManager.delDisable(sysApplication.getAppId());
        }

        //清除缓存
        this.sysApplicationManager.clearCache();

        return super.updateById(sysApplication);
    }

    private SysApplication checkedModifyPermissionAndGet(SysUser sysUser, int id) {
        SysApplication sysApplication = ServiceContext.me().getApplicationById(id);
        if (sysApplication == null) {
            sysApplication = getById(id);
        }
        AssertUtil.notNull(sysApplication, ServiceInterruptionException.fail(400, "参数错误"));
        AssertUtil.isTrue(sysUser.getUuid().equals(sysApplication.getCreatorUuid()), ServiceInterruptionException.fail(401, "无权操作"));
        return sysApplication;
    }
}
